{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3xW4O0zuKfWD"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "from openai import OpenAI, pydantic_function_tool\n",
        "import rich\n",
        "import requests\n",
        "import json\n",
        "from pydantic import BaseModel, Field\n",
        "from google.colab import userdata"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "api_key = userdata.get('OPENAI_API_KEY')\n",
        "MODEL = \"gpt-4o-mini\"\n",
        "\n",
        "openai = OpenAI(api_key=api_key)"
      ],
      "metadata": {
        "id": "vUydnLeLKs03"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "**Looping through tool calls in cases where they are received in multiple steps. For scenarios involving multiple tool calls, like GetWeather and SendEmail, where each tool might be called multiple times, we need to create logic that can send and receive messages from the model in multiple stages, making a final call to receive the complete response.**"
      ],
      "metadata": {
        "id": "blCx6gu1Vl46"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "The Pydantic-generated function structure is acceptable in OpenAI's Chat API, but the Responses API requires a slightly different structure."
      ],
      "metadata": {
        "id": "nznKPApuXC9z"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class GetWeather(BaseModel):\n",
        "    latitude: float = Field(..., description=\"Latitude of the location\")\n",
        "    longitude: float = Field(..., description=\"Longitude of the location\")\n",
        "\n",
        "class SendEmail(BaseModel):\n",
        "    to: str = Field(..., description=\"Email address of the recipient\")\n",
        "    subject: str = Field(..., description=\"Subject of the email\")\n",
        "    body: str = Field(..., description=\"Body of the email\")\n",
        "\n",
        "def get_weather(latitude, longitude):\n",
        "    response = requests.get(f\"https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}&current=temperature_2m,wind_speed_10m&hourly=temperature_2m,relative_humidity_2m,wind_speed_10m\")\n",
        "    data = response.json()\n",
        "    print(f\"get_weather function called to get weather for latitude = {latitude}, longitude = {longitude}\")\n",
        "    print(f\"And result is  = {data['current']['temperature_2m']}\")\n",
        "    return data['current']['temperature_2m']\n",
        "\n",
        "def send_email(to, subject, body):\n",
        "    print(f\"Tool send_email Sending email to {to} with subject {subject}\")\n",
        "    print(f\"Body: {body}\")\n",
        "    print(f\"Email Tool call completed\")\n",
        "    return \"Email Sent\"\n",
        "\n",
        "rich.print(pydantic_function_tool(GetWeather))\n",
        "rich.print(pydantic_function_tool(SendEmail))\n",
        "\n",
        "# creating list of tools here because it can be used in both Chat API and Responses API\n",
        "tools = [pydantic_function_tool(SendEmail), pydantic_function_tool(GetWeather)]"
      ],
      "metadata": {
        "id": "qvJhp-k5XEmW",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 577
        },
        "outputId": "116a4d62-e2e4-4f5a-e619-fa91c3c3ef85"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "\u001b[1m{\u001b[0m\n",
              "    \u001b[32m'type'\u001b[0m: \u001b[32m'function'\u001b[0m,\n",
              "    \u001b[32m'function'\u001b[0m: \u001b[1m{\u001b[0m\n",
              "        \u001b[32m'name'\u001b[0m: \u001b[32m'GetWeather'\u001b[0m,\n",
              "        \u001b[32m'strict'\u001b[0m: \u001b[3;92mTrue\u001b[0m,\n",
              "        \u001b[32m'parameters'\u001b[0m: \u001b[1m{\u001b[0m\n",
              "            \u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\n",
              "                \u001b[32m'latitude'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Latitude of the location'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Latitude'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'number'\u001b[0m\u001b[1m}\u001b[0m,\n",
              "                \u001b[32m'longitude'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Longitude of the location'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Longitude'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'number'\u001b[0m\u001b[1m}\u001b[0m\n",
              "            \u001b[1m}\u001b[0m,\n",
              "            \u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'latitude'\u001b[0m, \u001b[32m'longitude'\u001b[0m\u001b[1m]\u001b[0m,\n",
              "            \u001b[32m'title'\u001b[0m: \u001b[32m'GetWeather'\u001b[0m,\n",
              "            \u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m,\n",
              "            \u001b[32m'additionalProperties'\u001b[0m: \u001b[3;91mFalse\u001b[0m\n",
              "        \u001b[1m}\u001b[0m\n",
              "    \u001b[1m}\u001b[0m\n",
              "\u001b[1m}\u001b[0m\n"
            ],
            "text/html": [
              "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span>\n",
              "    <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'function'</span>,\n",
              "    <span style=\"color: #008000; text-decoration-color: #008000\">'function'</span>: <span style=\"font-weight: bold\">{</span>\n",
              "        <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'GetWeather'</span>,\n",
              "        <span style=\"color: #008000; text-decoration-color: #008000\">'strict'</span>: <span style=\"color: #00ff00; text-decoration-color: #00ff00; font-style: italic\">True</span>,\n",
              "        <span style=\"color: #008000; text-decoration-color: #008000\">'parameters'</span>: <span style=\"font-weight: bold\">{</span>\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'properties'</span>: <span style=\"font-weight: bold\">{</span>\n",
              "                <span style=\"color: #008000; text-decoration-color: #008000\">'latitude'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Latitude of the location'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Latitude'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'number'</span><span style=\"font-weight: bold\">}</span>,\n",
              "                <span style=\"color: #008000; text-decoration-color: #008000\">'longitude'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Longitude of the location'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Longitude'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'number'</span><span style=\"font-weight: bold\">}</span>\n",
              "            <span style=\"font-weight: bold\">}</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'required'</span>: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'latitude'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'longitude'</span><span style=\"font-weight: bold\">]</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'GetWeather'</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'object'</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'additionalProperties'</span>: <span style=\"color: #ff0000; text-decoration-color: #ff0000; font-style: italic\">False</span>\n",
              "        <span style=\"font-weight: bold\">}</span>\n",
              "    <span style=\"font-weight: bold\">}</span>\n",
              "<span style=\"font-weight: bold\">}</span>\n",
              "</pre>\n"
            ]
          },
          "metadata": {}
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "\u001b[1m{\u001b[0m\n",
              "    \u001b[32m'type'\u001b[0m: \u001b[32m'function'\u001b[0m,\n",
              "    \u001b[32m'function'\u001b[0m: \u001b[1m{\u001b[0m\n",
              "        \u001b[32m'name'\u001b[0m: \u001b[32m'SendEmail'\u001b[0m,\n",
              "        \u001b[32m'strict'\u001b[0m: \u001b[3;92mTrue\u001b[0m,\n",
              "        \u001b[32m'parameters'\u001b[0m: \u001b[1m{\u001b[0m\n",
              "            \u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\n",
              "                \u001b[32m'to'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Email address of the recipient'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'To'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\u001b[1m}\u001b[0m,\n",
              "                \u001b[32m'subject'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Subject of the email'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Subject'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\u001b[1m}\u001b[0m,\n",
              "                \u001b[32m'body'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Body of the email'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Body'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\u001b[1m}\u001b[0m\n",
              "            \u001b[1m}\u001b[0m,\n",
              "            \u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'to'\u001b[0m, \u001b[32m'subject'\u001b[0m, \u001b[32m'body'\u001b[0m\u001b[1m]\u001b[0m,\n",
              "            \u001b[32m'title'\u001b[0m: \u001b[32m'SendEmail'\u001b[0m,\n",
              "            \u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m,\n",
              "            \u001b[32m'additionalProperties'\u001b[0m: \u001b[3;91mFalse\u001b[0m\n",
              "        \u001b[1m}\u001b[0m\n",
              "    \u001b[1m}\u001b[0m\n",
              "\u001b[1m}\u001b[0m\n"
            ],
            "text/html": [
              "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span>\n",
              "    <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'function'</span>,\n",
              "    <span style=\"color: #008000; text-decoration-color: #008000\">'function'</span>: <span style=\"font-weight: bold\">{</span>\n",
              "        <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'SendEmail'</span>,\n",
              "        <span style=\"color: #008000; text-decoration-color: #008000\">'strict'</span>: <span style=\"color: #00ff00; text-decoration-color: #00ff00; font-style: italic\">True</span>,\n",
              "        <span style=\"color: #008000; text-decoration-color: #008000\">'parameters'</span>: <span style=\"font-weight: bold\">{</span>\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'properties'</span>: <span style=\"font-weight: bold\">{</span>\n",
              "                <span style=\"color: #008000; text-decoration-color: #008000\">'to'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Email address of the recipient'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'To'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span><span style=\"font-weight: bold\">}</span>,\n",
              "                <span style=\"color: #008000; text-decoration-color: #008000\">'subject'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Subject of the email'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Subject'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span><span style=\"font-weight: bold\">}</span>,\n",
              "                <span style=\"color: #008000; text-decoration-color: #008000\">'body'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Body of the email'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Body'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'string'</span><span style=\"font-weight: bold\">}</span>\n",
              "            <span style=\"font-weight: bold\">}</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'required'</span>: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'to'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'subject'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'body'</span><span style=\"font-weight: bold\">]</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'SendEmail'</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'object'</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'additionalProperties'</span>: <span style=\"color: #ff0000; text-decoration-color: #ff0000; font-style: italic\">False</span>\n",
              "        <span style=\"font-weight: bold\">}</span>\n",
              "    <span style=\"font-weight: bold\">}</span>\n",
              "<span style=\"font-weight: bold\">}</span>\n",
              "</pre>\n"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Chat Completion API\n",
        "\n",
        "https://platform.openai.com/docs/guides/function-calling?api-mode=chat"
      ],
      "metadata": {
        "id": "5wlqKfVBKyjD"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Defining a function that will receive list of messages and call the Chat API"
      ],
      "metadata": {
        "id": "EgXT84U9Tfqr"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Function to call OpenAI's Chat API\n",
        "def call_chat_api(messages):\n",
        "    response = openai.chat.completions.create(\n",
        "        model=MODEL,\n",
        "        messages=messages,\n",
        "        tools=tools\n",
        "    )\n",
        "    return response"
      ],
      "metadata": {
        "id": "dBrUkMCUKz0s"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Defining a function `handle_tool_call` in which we will loop through the tool_calls, invoke the corresponding functions, and update the response. Plus, we will append a new message for each function call to promptMessages."
      ],
      "metadata": {
        "id": "xvVxw5mGXWJ6"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def handle_tool_call(promptMessages, responseMessage):\n",
        "    for tool_call in responseMessage.tool_calls:\n",
        "        name = tool_call.function.name\n",
        "        args = json.loads(tool_call.function.arguments)\n",
        "        result = None\n",
        "        if name == \"SendEmail\":\n",
        "            result = send_email(**args)\n",
        "        elif name == \"GetWeather\":\n",
        "            result = get_weather(**args)\n",
        "        new_message = {\n",
        "            \"role\": \"tool\",\n",
        "            \"content\": str(result),\n",
        "            \"tool_call_id\": tool_call.id\n",
        "        }\n",
        "        promptMessages.append(new_message)"
      ],
      "metadata": {
        "id": "WItchxDLK2x6"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "The `handle_chat_api()` function handles communication with a chat API in a loop, processing user prompts, handling tool calls, and iterating until a final response is received."
      ],
      "metadata": {
        "id": "PeuUJSm9enc_"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def handle_chat_api(prompt):\n",
        "    messages=[\n",
        "        {\"role\": \"developer\", \"content\": \"You are a helpful assistant, you can send email about weather in a city. Email should be courteous and professional.\"},\n",
        "        {\"role\": \"user\", \"content\": prompt}\n",
        "    ]\n",
        "\n",
        "    response = call_chat_api(messages)\n",
        "    counter = 0 # counter to keep track of the number times we receive tool calls from model\n",
        "\n",
        "    while True:\n",
        "        print(\"Finish Reason = \",response.choices[0].finish_reason)\n",
        "        print(\"Tool Calls Length = \",len(response.choices[0].message.tool_calls or []))\n",
        "        print(\"Tool Calls = \",response.choices[0].message.tool_calls)\n",
        "        print(\"--\")\n",
        "        if(response.choices[0].finish_reason == \"stop\"):\n",
        "            print(\"-------\")\n",
        "            print(\"Number of times Tool calls received from model are = \",counter)\n",
        "            print()\n",
        "            print(response.choices[0].message.content)\n",
        "            print(\"-------\")\n",
        "            break\n",
        "        elif response.choices[0].finish_reason == \"tool_calls\":\n",
        "            counter += 1\n",
        "            messages.append(response.choices[0].message)\n",
        "            handle_tool_call(messages, response.choices[0].message)\n",
        "\n",
        "            # Uncomment below line to see the messages\n",
        "            # rich.print(messages)\n",
        "\n",
        "            # calling chat api again with updated messages and storing in same response variable\n",
        "            response = call_chat_api(messages)\n",
        "        print()\n"
      ],
      "metadata": {
        "id": "TFXLJ2KIbtqa"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Call `handle_chat_api` function with different inputs"
      ],
      "metadata": {
        "id": "9OLwJZYJe5qC"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Check result of each prompt\n",
        "\n",
        "# handle_chat_api(\"Send an email to shan@gmail.com and hello@gmail.com about weather in Karachi\")\n",
        "# handle_chat_api(\"Send an email about weather in Karachi to first@gmail.com and second@gmail.com\")\n",
        "handle_chat_api(\"send an email about weather in Karachi and Lahore to both shan@gmail.com and hello@gmail.com\")\n",
        "# handle_chat_api(\"send an email about weather in Karachi and Lahore to shan@gmail.com and hello@gmail.com\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "I5vmeSHqe8Nl",
        "outputId": "e0a44698-c8f8-4ed2-848a-f831624b4638"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Finish Reason =  tool_calls\n",
            "Tool Calls Length =  2\n",
            "Tool Calls =  [ChatCompletionMessageToolCall(id='call_xWIE6IRY2kUVM1gJWWE4tTOL', function=Function(arguments='{\"latitude\": 24.8607, \"longitude\": 67.0011}', name='GetWeather'), type='function'), ChatCompletionMessageToolCall(id='call_hUYWNhNnb1ec57fR9KlmAkTf', function=Function(arguments='{\"latitude\": 31.5497, \"longitude\": 74.3436}', name='GetWeather'), type='function')]\n",
            "--\n",
            "get_weather function called to get weather for latitude = 24.8607, longitude = 67.0011\n",
            "And result is  = 25.4\n",
            "get_weather function called to get weather for latitude = 31.5497, longitude = 74.3436\n",
            "And result is  = 22.4\n",
            "\n",
            "Finish Reason =  tool_calls\n",
            "Tool Calls Length =  1\n",
            "Tool Calls =  [ChatCompletionMessageToolCall(id='call_SNROPZ3KSUWRSmL7rCAZxrNe', function=Function(arguments='{\"to\":\"shan@gmail.com\",\"subject\":\"Current Weather Update for Karachi and Lahore\",\"body\":\"Dear Shan,\\\\n\\\\nI hope this message finds you well. \\\\n\\\\nI wanted to share the current weather updates for Karachi and Lahore:\\\\n\\\\n- **Karachi**: The temperature is currently around 25.4°C.\\\\n- **Lahore**: The temperature is currently around 22.4°C.\\\\n\\\\nPlease let me know if you need any further information.\\\\n\\\\nBest regards,\\\\n\\\\n[Your Name]\"}', name='SendEmail'), type='function')]\n",
            "--\n",
            "Tool send_email Sending email to shan@gmail.com with subject Current Weather Update for Karachi and Lahore\n",
            "Body: Dear Shan,\n",
            "\n",
            "I hope this message finds you well. \n",
            "\n",
            "I wanted to share the current weather updates for Karachi and Lahore:\n",
            "\n",
            "- **Karachi**: The temperature is currently around 25.4°C.\n",
            "- **Lahore**: The temperature is currently around 22.4°C.\n",
            "\n",
            "Please let me know if you need any further information.\n",
            "\n",
            "Best regards,\n",
            "\n",
            "[Your Name]\n",
            "Email Tool call completed\n",
            "\n",
            "Finish Reason =  tool_calls\n",
            "Tool Calls Length =  1\n",
            "Tool Calls =  [ChatCompletionMessageToolCall(id='call_BDwihwwq9mDhRETh46hvdY7g', function=Function(arguments='{\"to\":\"hello@gmail.com\",\"subject\":\"Current Weather Update for Karachi and Lahore\",\"body\":\"Dear Sir/Madam,\\\\n\\\\nI hope this message finds you well. \\\\n\\\\nI wanted to share the current weather updates for Karachi and Lahore:\\\\n\\\\n- **Karachi**: The temperature is currently around 25.4°C.\\\\n- **Lahore**: The temperature is currently around 22.4°C.\\\\n\\\\nPlease let me know if you need any further information.\\\\n\\\\nBest regards,\\\\n\\\\n[Your Name]\"}', name='SendEmail'), type='function')]\n",
            "--\n",
            "Tool send_email Sending email to hello@gmail.com with subject Current Weather Update for Karachi and Lahore\n",
            "Body: Dear Sir/Madam,\n",
            "\n",
            "I hope this message finds you well. \n",
            "\n",
            "I wanted to share the current weather updates for Karachi and Lahore:\n",
            "\n",
            "- **Karachi**: The temperature is currently around 25.4°C.\n",
            "- **Lahore**: The temperature is currently around 22.4°C.\n",
            "\n",
            "Please let me know if you need any further information.\n",
            "\n",
            "Best regards,\n",
            "\n",
            "[Your Name]\n",
            "Email Tool call completed\n",
            "\n",
            "Finish Reason =  stop\n",
            "Tool Calls Length =  0\n",
            "Tool Calls =  None\n",
            "--\n",
            "-------\n",
            "Number of times Tool calls received from model are =  3\n",
            "\n",
            "I have sent the weather update emails for Karachi and Lahore to both Shan and the email address hello@gmail.com. If you need any further assistance, feel free to ask!\n",
            "-------\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Responses API\n",
        "\n",
        "https://platform.openai.com/docs/guides/function-calling?api-mode=responses"
      ],
      "metadata": {
        "id": "tCzSosP5K-JR"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "To use pydantic-generated function, we need to use `openai.responses.parse()` function call\n",
        "\n",
        "https://github.com/openai/openai-python/blob/main/examples/responses/structured_outputs_tools.py"
      ],
      "metadata": {
        "id": "X__yLWt7aIOU"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Using old way of sending history messages in every call"
      ],
      "metadata": {
        "id": "p87WEKKmcB9L"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Example of new way of handling messages is in separate notebook"
      ],
      "metadata": {
        "id": "zq6ZsalTfcSh"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Defining a function that will receive list of messages and call the Responses API"
      ],
      "metadata": {
        "id": "bsFB-WT1feBz"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Function to call OpenAI's Chat API\n",
        "def call_response_api(messages):\n",
        "    response = openai.responses.parse(\n",
        "        model=MODEL,\n",
        "        input=messages,\n",
        "        tools = tools\n",
        "    )\n",
        "    return response"
      ],
      "metadata": {
        "id": "gUjC_PQCcETm"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Defining a function `handle_tool_call_responses` in which we will loop through the tool_calls, invoke the corresponding functions, and update the response. Plus, we will append a new message for each function call to promptMessages."
      ],
      "metadata": {
        "id": "V7TeuUMvcJCY"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def handle_tool_call_responses(promptMessages, responseFromAPI):\n",
        "    isFunctionCall = False\n",
        "    for tool_call in responseFromAPI.output:\n",
        "        if tool_call.type == \"function_call\": # check if the output's type is function_call\n",
        "            # As we don't have finish_reason property in Responses API that indicate tool_calls or stop\n",
        "            # so we will use isFunctionCall flag to keep track it function call is received\n",
        "            isFunctionCall = True\n",
        "            name = tool_call.name\n",
        "            arguments = json.loads(tool_call.arguments)\n",
        "            if name == \"SendEmail\":\n",
        "                result = send_email(**arguments)\n",
        "            elif name == \"GetWeather\":\n",
        "                result = get_weather(**arguments)\n",
        "            new_message = {\n",
        "                \"type\": \"function_call_output\",\n",
        "                \"call_id\": tool_call.call_id,\n",
        "                \"output\": str(result)\n",
        "            }\n",
        "            # In case of Response API we need to append individual output messages to messages list\n",
        "            # Therefore, we append tool_call and new_message to promptMessages list one by one\n",
        "            # Plus we need to remove the parsed_arguments from tool_call object\n",
        "            del tool_call.parsed_arguments\n",
        "            promptMessages.append(tool_call)\n",
        "            promptMessages.append(new_message)\n",
        "    return isFunctionCall"
      ],
      "metadata": {
        "id": "ds6RgacxcL8a"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "The `handle_response_api()` function handles communication with a Responses API in a loop, processing user prompts, handling tool calls, and iterating until a final response is received."
      ],
      "metadata": {
        "id": "FSgCYJWaf0N8"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def handle_response_api(prompt):\n",
        "    messages=[\n",
        "        {\"role\": \"developer\", \"content\": \"You are a helpful assistant, you can send email about weather in a city. Email should be courteous and professional.\"},\n",
        "        {\"role\": \"user\", \"content\": prompt}\n",
        "    ]\n",
        "\n",
        "    response = call_response_api(messages)\n",
        "    counter = 0 # counter to keep track of the number times we receive tool calls from model\n",
        "\n",
        "    while True:\n",
        "        print()\n",
        "        print(\"Iteration Started\")\n",
        "        print(\"Response Status = \",response.status)\n",
        "        # rich.print(\"response.output = \",response.output)\n",
        "        # We don't have finish_reason in response from Response API, and status always be \"completed\"\n",
        "        isFunctionCalled = handle_tool_call_responses(messages, response)\n",
        "        if isFunctionCalled:\n",
        "            counter += 1\n",
        "            # Uncomment below line to see the messages\n",
        "            # rich.print(messages)\n",
        "            response = call_response_api(messages)\n",
        "        else:\n",
        "            print(\"-------\")\n",
        "            print(\"Number of times Tool calls received from model are = \",counter)\n",
        "            print()\n",
        "            print(response.output_text)\n",
        "            print(\"-------\")\n",
        "            break"
      ],
      "metadata": {
        "id": "6yllPhl4f0fJ"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Call `handle_response_api` function with different inputs"
      ],
      "metadata": {
        "id": "DaiKz9TXf77h"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Check result of each prompt\n",
        "handle_response_api(\"Send an email to shan@gmail.com and hello@gmail.com about weather in Karachi\")\n",
        "# handle_response_api(\"Send an email about weather in Karachi to first@gmail.com and second@gmail.com\") # This prompt is giving error somehow\n",
        "# handle_response_api(\"send an email about weather in Karachi and Lahore to both shan@gmail.com and hello@gmail.com\")\n",
        "# handle_response_api(\"send an email about weather in Karachi and Lahore to shan@gmail.com and hello@gmail.com\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "9u6Fog3bf8MU",
        "outputId": "12f5a261-3f5d-4adf-aa5b-1fde23e74873"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\n",
            "Iteration Started\n",
            "Response Status =  completed\n",
            "get_weather function called to get weather for latitude = 24.8607, longitude = 67.0011\n",
            "And result is  = 25.4\n",
            "\n",
            "Iteration Started\n",
            "Response Status =  completed\n",
            "Tool send_email Sending email to shan@gmail.com with subject Weather Update for Karachi\n",
            "Body: Dear Shan,\n",
            "\n",
            "I hope this email finds you well. \n",
            "\n",
            "I wanted to provide you with a brief weather update for Karachi. The current temperature is approximately 25.4°C. \n",
            "\n",
            "Please let me know if you need any further information.\n",
            "\n",
            "Best regards,\n",
            "\n",
            "Your Assistant\n",
            "Email Tool call completed\n",
            "\n",
            "Iteration Started\n",
            "Response Status =  completed\n",
            "Tool send_email Sending email to hello@gmail.com with subject Weather Update for Karachi\n",
            "Body: Dear [Recipient's Name],\n",
            "\n",
            "I hope this email finds you well. \n",
            "\n",
            "I wanted to provide you with a brief weather update for Karachi. The current temperature is approximately 25.4°C. \n",
            "\n",
            "Please let me know if you need any further information.\n",
            "\n",
            "Best regards,\n",
            "\n",
            "Your Assistant\n",
            "Email Tool call completed\n",
            "\n",
            "Iteration Started\n",
            "Response Status =  completed\n",
            "-------\n",
            "Number of times Tool calls received from model are =  3\n",
            "\n",
            "I have sent the emails to both Shan and Hello regarding the weather in Karachi. If you need any further assistance, feel free to ask!\n",
            "-------\n"
          ]
        }
      ]
    }
  ]
}